package client

import (
	"bytes"
	"context"
	"crypto/tls"
	"io"
	"log"
	"net/http"
	"time"
)

const (
	ENV_HTTP_TMOUT_SECONDS     = "HTTP_TMOUT_SECONDS"
	DEFAULT_HTTP_TMOUT_SECONDS = 30
)

type HttpOption func(c *http.Client)

func WithTimeout(timeout time.Duration) HttpOption {
	return func(c *http.Client) {
		c.Timeout = timeout
	}
}

func TlsInsecureSkipVerify() HttpOption {
	return func(c *http.Client) {
		c.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}
}

func NewHttpClient(opts ...HttpOption) *simpleCaller {

	c := http.Client{
		Timeout: DEFAULT_HTTP_TMOUT_SECONDS * time.Second,
	}
	for _, opt := range opts {
		opt(&c)
	}

	return &simpleCaller{
		client: &c,
	}
}

// simpleCaller 使用默认http client
type simpleCaller struct {
	client *http.Client
}

func (c *simpleCaller) Post(ctx context.Context, url string, header http.Header, body []byte) *Response {
	return c.do(ctx, http.MethodPost, url, header, body)
}

func (c *simpleCaller) Get(ctx context.Context, url string, header http.Header) *Response {
	return c.do(ctx, http.MethodGet, url, header, nil)
}

// just adapte ctgpaa api that need empty body...
func (c *simpleCaller) GetWithEmptyBody(ctx context.Context, url string, header http.Header) *Response {
	return c.do(ctx, http.MethodGet, url, header, []byte("{}"))
}

func (c *simpleCaller) Patch(ctx context.Context, url string, header http.Header, body []byte) *Response {
	return c.do(ctx, http.MethodPatch, url, header, body)
}

func (c *simpleCaller) Put(ctx context.Context, url string, header http.Header, body []byte) *Response {
	return c.do(ctx, http.MethodPut, url, header, body)
}

func (c *simpleCaller) Delete(ctx context.Context, url string, header http.Header, body []byte) *Response {
	return c.do(ctx, http.MethodDelete, url, header, body)
}

func (c *simpleCaller) do(ctx context.Context, method, uurl string, header http.Header, body []byte) *Response {

	log.Printf("[CTAPI] call: %s %s body:%s", method, uurl, string(body))

	resp, err := c.request(ctx, method, uurl, header, body)
	if err != nil {
		log.Printf("[CTAPI] call: %s %s err :%s", method, uurl, err.Error())
		return &Response{
			Error: &Error{
				StatusCode: http.StatusInternalServerError,
				Msg:        err.Error(),
			},
		}
	}
	defer resp.Body.Close()

	log.Printf("[CTAPI] call: %s %s http status code %d", method, uurl, resp.StatusCode)

	var respBody []byte
	if respBody, err = io.ReadAll(resp.Body); err != nil {
		log.Printf("[CTAPI] read body error: %s", err)
		return &Response{
			Error: &Error{
				StatusCode: http.StatusInternalServerError,
				Msg:        err.Error(),
			},
		}
	}

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		log.Printf("[CTAPI] call fail , status code: %d , resp body: %s", resp.StatusCode, string(respBody))
		return &Response{
			Error: &Error{
				StatusCode: resp.StatusCode,
				Msg:        string(respBody),
			},
		}
	}

	return &Response{
		Error: nil,
		Body:  respBody,
	}
}

func (c *simpleCaller) request(ctx context.Context, method, uurl string, header http.Header, body []byte) (*http.Response, error) {
	var err error
	req, err := http.NewRequestWithContext(ctx, method, uurl, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header = header

	var resp *http.Response
	resp, err = c.client.Do(req)

	return resp, err
}
